﻿
#pragma once

#include <QtCharts/qchart.h>
#include <QtCharts/qchartview.h>
#include <QtCharts/qlineseries.h>
#include <QtCore/qsettings.h>
#include <QtCore/qstring.h>
#include <QtGui/qevent.h>
#include <QtWidgets/qgraphicsview.h>
#include <QtWidgets/qmainwindow.h>
#include <QtWidgets/qscrollbar.h>


class MouseHandler4GraphicsView : QObject {
    // Q_OBJECT

    // signals:
    //  void left_clicked(QMouseEvent *event);

   private:
    QGraphicsView *_v;
    QPointF _last_mouse_point;
    Qt::CursorShape _last_cursor_shape = Qt::ArrowCursor;
    // bool _drag_by_right_button = true;  // 不提供切换左右键控制的选项，因为会很麻烦
    bool _left_pressed = false;
    bool _right_pressed = false;
    bool _cancel_left_click_when_drag = true;
    bool _use_drag_cursor = true;

   public:
    explicit MouseHandler4GraphicsView(QGraphicsView *v, QObject *parent = nullptr) :
        _v(v), QObject{parent} {}

    void enable_auto_cancel_left_click_when_drag(bool val) {
        _cancel_left_click_when_drag = val;
    }

    void enable_drag_cursor(bool val) {
        _use_drag_cursor = val;
    }

    QPointF get_last_mouse_point() {
        return _last_mouse_point;
    }

    bool left_pressed() {
        return _left_pressed;
    }

    bool right_pressed() {
        return _right_pressed;
    }


    void handle_mouse_press_event(QMouseEvent *event) {
        if (event->button() == Qt::RightButton) {
            _right_pressed = true;
            _last_mouse_point = event->position();

            if (_use_drag_cursor) {
                _last_cursor_shape = _v->cursor().shape();
                _v->setCursor(QCursor(Qt::OpenHandCursor));
            }
        }

        if (event->button() == Qt::LeftButton) {
            _left_pressed = true;
        }
    }

    /**
     *
     * @return true   检测到左键 click
     * @return false  未发生左键 click
     */
    bool handle_mouse_release_event(QMouseEvent *event) {
        auto b = event->button();
        if (b == Qt::RightButton) {
            _right_pressed = false;
            if (_use_drag_cursor) {
                _v->setCursor(QCursor(_last_cursor_shape));
            }
        }

        if (b == Qt::LeftButton) {
            _left_pressed = false;
            return true;
        }

        return false;
    }


    void scroll_by_point(QPoint delta) {
        auto delta_x = delta.x();
        auto delta_y = delta.y();
        auto *v_bar = _v->verticalScrollBar();
        auto vy = v_bar->value();
        v_bar->setValue(vy - delta_y);
        auto *h_bar = _v->horizontalScrollBar();
        auto vx = h_bar->value();
        h_bar->setValue(vx - delta_x);
    }

    void scroll_by_point(QPointF p0, QPointF p1) {
        scroll_by_point((p1 - p0).toPoint());
    }

    /**
     * @brief handles mouseMoveEvent
     *
     * event.accept() 要由调用者执行
     */
    QPointF handle_mouse_move(QMouseEvent *event, bool drag = true) {
        auto view_pos = event->position();
        if (_left_pressed) {
            if (_cancel_left_click_when_drag) {
                _left_pressed = false;
            }
        }
        else if (_right_pressed) {
            QPointF delta = view_pos - _last_mouse_point;
            if (drag) {
                scroll_by_point(delta.toPoint());
            }

            _last_mouse_point = view_pos;
            return delta;
        }

        return {0.0f, 0.0f};
    }


    /**
     * @brief handles mouseWheelEvent
     */
    void handle_wheel_zoom(QWheelEvent *event) {
        _v->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
        auto delta = event->angleDelta().y();
        float factor = 1.15;
        if (delta > 0) {
            // Zoom in
            _v->scale(factor, factor);
        }
        else {
            // Zoom out
            _v->scale(1.0f / factor, 1.0f / factor);
        }
    }

    // Q_DISABLE_COPY(MouseHandler4GraphicsView);
};


class ZoomableChartView : public QChartView {
    Q_OBJECT

   private:
    MouseHandler4GraphicsView _mh;
    bool _zoomed_before_scroll = false;
    bool _zoom_horizontal = true;
    bool _zoom_vertical = true;

   public slots:
    void resetChart() {
        auto *chart = this->chart();
        chart->zoomReset();  // 在拖动chart 之前先做微小的缩放，从而在缩放复位时，拖动也会被复位
        _zoomed_before_scroll = false;
    }

    void enableVerticalZoom(bool val) {
        _zoom_vertical = val;
    }

    void enableHorizontalZoom(bool val) {
        _zoom_horizontal = val;
    }

   public:
    explicit ZoomableChartView(QWidget *parent = nullptr) :
        QChartView{parent}, _mh{this} {
    }


    virtual void mousePressEvent(QMouseEvent *event) override {
        _mh.handle_mouse_press_event(event);
    }

    virtual void mouseReleaseEvent(QMouseEvent *event) override {
        _mh.handle_mouse_release_event(event);
    }

    virtual void mouseMoveEvent(QMouseEvent *event) override {
        if (!_zoomed_before_scroll) {
            this->chart()->zoom(0.9999);
            _zoomed_before_scroll = true;
        }

        QPointF delta = _mh.handle_mouse_move(event, false);
        this->chart()->scroll(-delta.x(), delta.y());
    }

    virtual void wheelEvent(QWheelEvent *event) override {
        // CREDIT: https://blog.csdn.net/weixin_41882459/article/details/107713316
        // 原作者不明 ㄟ( ▔, ▔ )ㄏ

        if (!(_zoom_horizontal || _zoom_vertical))
            return;

        _zoomed_before_scroll = true;

        // 设置比例
        float ratio = 1.15f;
        if (event->angleDelta().y() > 0) {
            ratio = 1 / ratio;
        }

        // 1. 读取视图基本信息
        auto *chart = this->chart();
        QRectF plot_area = this->chart()->plotArea();
        QPointF center = plot_area.center();

        // 2. 水平调整
        if (_zoom_horizontal) {
            plot_area.setWidth(plot_area.width() * ratio);
        }
        // 3. 竖直调整
        if (_zoom_vertical) {
            plot_area.setHeight(plot_area.height() * ratio);
        }

        // 4.3 计算视点，让鼠标点击的位置尽量保持不动(等比换算，存在一点误差)
        auto event_pos = event->position();
        QPointF new_center = (center * 2.0f) - event_pos - (center - event_pos) / ratio;

        //  5. 设置视点
        plot_area.moveCenter(new_center);

        // 6. 提交缩放调整
        chart->zoomIn(plot_area);
        //__super::wheelEvent(pEvent);
    }


    Q_DISABLE_COPY(ZoomableChartView);
};